package com.ling.plasticene.recyclerview.loadmore;

import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import com.ling.plasticene.recyclerview.WrapAdapter;


/**
 * Function: Adapter With Scroll Load More Function
 *
 * ==============================================================
 * Example:
 *
 * mRecyclerView = (RecyclerView)findViewById(R.id.recyclerView);
 * mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
 *
 * final LoadMoreAdapter loadMoreAdapter = new LoadMoreAdapter(adapter, new DefaultLoadMoreView(this));
 * loadMoreAdapter.setLoadMoreListener(new LoadMore.OnLoadMoreListener() {
 *      @Override
 *      public void onLoadMore() {
 *          //Custom action
 *
 *          loadMoreAdapter.setState(Stateable.NORMAL);  //or your custom state, just supported in your custom load more view
 *      }
 * });
 *
 * mRecyclerView.setAdapter(loadMoreAdapter);
 *
 * ==============================================================
 *
 * Created by ling(quan.ling@hotmail.com) on 15/11/3.
 */
public class LoadMoreAdapter extends WrapAdapter implements LoadMore{
    private static final String TAG = "LoadMoreAdapter";
    private static final boolean DEBUG = false;

    public static final int TYPE_LOAD_MORE = 0xFFFEEE;

    /**
     * Load More View 接口，自定义View实现该接口
     */
    public interface LoadMoreUIHandler {
        /**
         * 获取View
         * @return
         */
        View getView();

        /**
         * 设置状态
         * @param loadMoreAdapter
         * @param state
         */
        void setState(LoadMoreAdapter loadMoreAdapter, int state);
    }

    protected LAYOUT_MANAGER_TYPE layoutManagerType;
    private OnLoadMoreListener mLoadMoreListener;
    private LoadMoreViewHolder mLoadMoreViewHolder;
    private int mState = NORMAL;
    private int mOriginItemCount;

    private int lastVisibleItemPosition;

    /**
     * 滚动时是否自动加载更多
     */
    private boolean isAutoLoadWhenScroll = true;

    /**
     * 倒数第几个开始加载更多; 默认最后一个
     */
    private int mReverseCount = 0;

    @SuppressWarnings("unchecked")
    public LoadMoreAdapter(RecyclerView.Adapter originAdapter, LoadMoreUIHandler loadMoreUIHandler) {
        super(originAdapter);

        mLoadMoreViewHolder = new LoadMoreViewHolder(loadMoreUIHandler);
    }

    /**
     * 设置加载更多事件监听器
     * @param loadMoreListener
     */
    @Override
    public void setLoadMoreListener(OnLoadMoreListener loadMoreListener) {
        mLoadMoreListener = loadMoreListener;
    }

    /**
     * 设置滑到倒数第几个开始加载更多
     * @param reverseCount 个数，0：最后一个
     */
    public void setReverseCount(int reverseCount) {
        mReverseCount = reverseCount;
    }

    public void setAutoLoad(boolean isAutoLoadWhenScroll) {
        this.isAutoLoadWhenScroll = isAutoLoadWhenScroll;
    }

    /**
     * 更改加载更多状态
     *
     * @param state LoadingMoreStatus
     */
    @UiThread
    public void setState(int state) {
        if (DEBUG)
            Log.d(TAG, "setState " + state);

        if (mState == state) {
            return;
        }

        mState = state;
        switch (mState) {
            case NORMAL:
            case EMPTY:
                notifyDataSetChanged();
                break;
            case LOADING:
                notifyLoadMoreViewChanged();
                if (mLoadMoreListener != null)
                    mLoadMoreListener.onLoadMore();
                break;
            case ERROR:
                notifyLoadMoreViewChanged();
                break;
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        switch (viewType) {
            case TYPE_LOAD_MORE:
                return mLoadMoreViewHolder;

            default:
                return super.onCreateViewHolder(viewGroup, viewType);
        }
    }

    @SuppressWarnings("unchecked viewHolder type")
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
        int viewType = getItemViewType(position);
        switch (viewType) {
            case TYPE_LOAD_MORE:
                onLoadMoreBindViewHolder(viewHolder);
                break;

            default:
                super.onBindViewHolder(viewHolder, position);
                break;
        }

        if (viewHolder.itemView.getLayoutParams() instanceof StaggeredGridLayoutManager.LayoutParams) {
            StaggeredGridLayoutManager.LayoutParams layoutParams =
                    (StaggeredGridLayoutManager.LayoutParams) viewHolder.itemView.getLayoutParams();
            layoutParams.setFullSpan(position == getLoadMorePosition());
        }
    }

    @Override
    public int getItemCount() {
        mOriginItemCount = super.getItemCount();
        return mOriginItemCount == 0 ? mOriginItemCount : mOriginItemCount + 1;
    }

    @Override
    public int getItemViewType(int position) {
        if (position == getLoadMorePosition()) {
            return TYPE_LOAD_MORE;
        }
        int viewType = super.getItemViewType(position);
        if (viewType == TYPE_LOAD_MORE) {
            throw new IllegalArgumentException("These viewTypes is for special case!");
        }
        return viewType;
    }

    protected void onLoadMoreBindViewHolder(RecyclerView.ViewHolder viewHolder) {
        ((LoadMoreViewHolder) viewHolder).updateState();
    }

    public enum LAYOUT_MANAGER_TYPE {
        LINEAR,
        GRID,
        STAGGERED_GRID
    }

    private RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() {
        private int[] lastPositions;

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);

            if (!isAutoLoadWhenScroll || mState != NORMAL) {
                return;
            }

            RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();

            if (layoutManagerType == null) {
                if (layoutManager instanceof GridLayoutManager) {
                    layoutManagerType = LAYOUT_MANAGER_TYPE.GRID;
                } else if (layoutManager instanceof LinearLayoutManager) {
                    layoutManagerType = LAYOUT_MANAGER_TYPE.LINEAR;
                } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                    layoutManagerType = LAYOUT_MANAGER_TYPE.STAGGERED_GRID;
                } else {
                    throw new RuntimeException("Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager");
                }
            }

            switch (layoutManagerType) {
                case LINEAR:
                case GRID:
                    lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
                    break;
                case STAGGERED_GRID:
                    StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
                    if (lastPositions == null)
                        lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];

                    staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
                    lastVisibleItemPosition = findMax(lastPositions);

                    staggeredGridLayoutManager.findFirstVisibleItemPositions(lastPositions);
                    break;
            }

            if (DEBUG) {
                Log.d(TAG, "lastVisibleItemPosition: " + lastVisibleItemPosition
                        + " mOriginItemCount - mReverseCount: " + (mOriginItemCount - mReverseCount));
            }

            if (mState != LOADING && lastVisibleItemPosition >= mOriginItemCount - mReverseCount) {
                setState(LOADING);
            }
        }

    };

    private int findMax(int[] lastPositions) {
        int max = Integer.MIN_VALUE;
        for (int value : lastPositions) {
            if (value > max)
                max = value;
        }
        return max;
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);

        recyclerView.setHasFixedSize(true);
        recyclerView.addOnScrollListener(mOnScrollListener);

        //wrap origin SpanSizeLookup
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
            gridLayoutManager.setSpanSizeLookup(new LoadMoreSpanSizeLookup(gridLayoutManager));
        }

        recyclerView.getAdapter().registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
            @Override
            public void onChanged() {
                super.onChanged();
            }
        });
    }

    @Override
    public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
        super.onDetachedFromRecyclerView(recyclerView);

        recyclerView.removeOnScrollListener(mOnScrollListener);

        //remove wrap SpanSizeLookup
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
            gridLayoutManager.setSpanSizeLookup(((LoadMoreSpanSizeLookup) gridLayoutManager.getSpanSizeLookup()).mOriginSpanSizeLookup);
        }
    }

    protected void notifyLoadMoreViewChanged() {
        notifyItemChanged(getLoadMorePosition());
    }

    int getLoadMorePosition() {
        return mOriginItemCount;
    }

    public class LoadMoreViewHolder extends RecyclerView.ViewHolder {
        LoadMoreUIHandler mLoadMoreUIHandler;

        public LoadMoreViewHolder(LoadMoreUIHandler loadMoreUIHandler) {
            super(loadMoreUIHandler.getView());

            mLoadMoreUIHandler = loadMoreUIHandler;
        }

        public void updateState() {
            mLoadMoreUIHandler.setState(LoadMoreAdapter.this, mState);
        }
    }

    public class LoadMoreSpanSizeLookup extends GridLayoutManager.SpanSizeLookup {
        GridLayoutManager.SpanSizeLookup mOriginSpanSizeLookup;

        int mSpanCount;

        public LoadMoreSpanSizeLookup(@NonNull GridLayoutManager gridLayoutManager) {
            mOriginSpanSizeLookup = gridLayoutManager.getSpanSizeLookup();
            mSpanCount = gridLayoutManager.getSpanCount();
        }

        @Override
        public void setSpanIndexCacheEnabled(boolean cacheSpanIndices) {
            mOriginSpanSizeLookup.setSpanIndexCacheEnabled(cacheSpanIndices);
        }

        @Override
        public void invalidateSpanIndexCache() {
            mOriginSpanSizeLookup.invalidateSpanIndexCache();
        }

        @Override
        public boolean isSpanIndexCacheEnabled() {
            return mOriginSpanSizeLookup.isSpanIndexCacheEnabled();
        }

        @Override
        public int getSpanIndex(int position, int spanCount) {
            if (position == getLoadMorePosition()) {
                return super.getSpanIndex(position, spanCount);
            } else {
                return mOriginSpanSizeLookup.getSpanIndex(position, spanCount);
            }
        }

        @Override
        public int getSpanGroupIndex(int adapterPosition, int spanCount) {
            if (adapterPosition == getLoadMorePosition()) {
                return super.getSpanGroupIndex(adapterPosition, spanCount);
            } else {
                return mOriginSpanSizeLookup.getSpanGroupIndex(adapterPosition, spanCount);
            }
        }

        @Override
        public int getSpanSize(int position) {
            if (position == getLoadMorePosition()) {
                return mSpanCount;
            } else {
                return mOriginSpanSizeLookup.getSpanSize(position);
            }
        }
    }

}
